using System;
using System.Xml;

namespace UnitTests
{
	/// <summary>
	/// Reads & writes data in xml file with XPath that doesn't exists yet.
	/// </summary>
	public class XPathStore
	{
		protected XmlDocument doc;
		protected string sXmlFileName;

		private XPathStore()
		{

		}


		/// <summary>
		/// Get a xml node from xPath, if it doesn,t exists create it.
		///
		/// </summary>
		/// <param name="sXPath"></param>
		/// <param name="doc">The XmlDocument to search/write</param>
		/// <returns></returns>
		public static XmlNode QueryCreatNode(XmlDocument doc, string sXPath)
		{
			Console.WriteLine("Query xml document with Xpath={0}", sXPath);
			XmlNode node = null;

			try
			{
				XmlNodeList list = doc.SelectNodes(sXPath);
				if (list.Count == 1)
					return list.Item(0); // The easy way (The node exists)
				else if (list.Count == 0)
				{
					Console.WriteLine("	ACTION) create XPath to node");

					// Parses XPath in separate parts.
					bool bParsingRandomText = false;
					int nXpathPartStart = -1;

					for (int i = 0; i < sXPath.Length; i++)
					{
						if (!bParsingRandomText &&
						    ((sXPath[i] == '/' || sXPath[i] == '\0') || i == sXPath.Length - 1) &&
						    nXpathPartStart != -1)
						{
							if (i == sXPath.Length - 1)
								i++; // Include last caracter

							// handle this XPath part
							node = QueryCreateNode(doc, node, sXPath.Substring(nXpathPartStart, i - nXpathPartStart));

							nXpathPartStart = -1;
						}
						else if (sXPath[i] == '\"')
							bParsingRandomText = !bParsingRandomText; //  After '"' every character is possible, even '/'
						else if (nXpathPartStart == -1)
							nXpathPartStart = i;
					}
				}
				else
				{
					// ERROR (only one node expected)
					Console.WriteLine("ERROR   Xpath:{0}  returns {1} nodes\n	Change XPath or XML so it returns ony one node", sXPath,
					                  list.Count);
				}
			}
			catch (Exception e)
			{
				Console.WriteLine("Exception: {0}", e);
				return null;
			}

			return node;
		}


		// Create the required node for XPath
		protected static XmlNode QueryCreateNode(XmlDocument doc, XmlNode node, string sXPathPart)
		{
			if (node == null)
				node = doc;
			Console.WriteLine("	HANDLE XPathPart) current node={0}    XPath={1}", node.Name, sXPathPart);

			//------------------------------------------------------------------------------------
			// Step 1)	Handle select part
			int nNodeStart = -1;
			int nAttrStart = -1;
			int nLoc = 0;

			for (; nLoc < sXPathPart.Length; nLoc++)
			{
				if (nAttrStart == -1 && nNodeStart == -1)
				{
					if (sXPathPart[nLoc] == '@')
						nAttrStart = nLoc + 1;
					else
						nNodeStart = nLoc;
				}

				if (sXPathPart[nLoc] == '[' || nLoc == sXPathPart.Length - 1)
					break;
			}
			if (nLoc == sXPathPart.Length - 1)
				nLoc++; // Include last caracter


			if (nNodeStart != -1)
			{
				Console.WriteLine("		ACTION) query node={0}   with XPath={1}", sXPathPart.Substring(nNodeStart, nLoc - nNodeStart),
				                  sXPathPart);

				// Query child Node
				XmlNodeList NodeList = node.SelectNodes(sXPathPart);
				if (NodeList.Count == 1)
					return NodeList.Item(0);
				else if (NodeList.Count == 0)
				{
					Console.WriteLine("		ACTION) create node={0}", sXPathPart.Substring(nNodeStart, nLoc - nNodeStart));
					node = node.AppendChild(doc.CreateElement(sXPathPart.Substring(nNodeStart, nLoc - nNodeStart)));
				}
				else
					Console.WriteLine("ERROR   XpathPart:{0}  returns {1} nodes\n	Change XPath or XML so it returns ony one node",
					                  sXPathPart, NodeList.Count);
			}
			else if (nAttrStart != -1)
			{
				// Get Attribute
				Console.WriteLine("		ACTION) Query node={0} for  Attribute={1}", node.Name,
				                  sXPathPart.Substring(nAttrStart, nLoc - nAttrStart));
				XmlAttributeCollection AttrCol = node.Attributes;
				node = AttrCol.GetNamedItem(sXPathPart.Substring(nAttrStart, nLoc - nAttrStart));
				if (node == null)
				{
					Console.WriteLine("		ACTION) create attibute={0}", sXPathPart.Substring(nAttrStart, nLoc - nAttrStart));
					node = AttrCol.SetNamedItem(doc.CreateAttribute(sXPathPart.Substring(nAttrStart, nLoc - nAttrStart)));
				}
			}


			//------------------------------------------------------------------------------------
			// Step 2)	Handle filter parts
			int nAttrNameStart = -1;
			int nAttrTextStart = -1;

			for (; nLoc < sXPathPart.Length; nLoc++)
			{
				if (sXPathPart[nLoc] == '@')
					nAttrNameStart = nLoc + 1;
				else if (sXPathPart[nLoc] == '\"')
				{
					if (nAttrTextStart == -1)
						nAttrTextStart = nLoc + 1;
					else
					{
						Console.WriteLine("		ACTION) query node={0} for attribute={1}", node.Name,
						                  sXPathPart.Substring(nAttrNameStart, nAttrTextStart - nAttrNameStart - 2));
						XmlNode attrNode = node.Attributes.GetNamedItem(sXPathPart.Substring(nAttrNameStart, nLoc - nAttrNameStart));
						if (attrNode == null)
						{
							Console.WriteLine("		ACTION) create attibute={0}",
							                  sXPathPart.Substring(nAttrNameStart, nAttrTextStart - nAttrNameStart - 2));
							attrNode = doc.CreateAttribute(sXPathPart.Substring(nAttrNameStart, nAttrTextStart - nAttrNameStart - 2));
							attrNode.Value = sXPathPart.Substring(nAttrTextStart, nLoc - nAttrTextStart);
							node.Attributes.SetNamedItem(attrNode);
						}

						nAttrNameStart = -1;
						nAttrTextStart = -1;
					}
				}
				else if (sXPathPart[nLoc] == ']')
					break;
			}

			return node;
		}
	}
}